/////////////////////////////////////////////////////////////////////////////
// Name:        imageblock.cpp
// Purpose:     A block of image data
// Author:      Julian Smart
// Modified by:
// Created:     2006-05-11
// RCS-ID:      $$
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/image.h"
#include "wx/dataobj.h"
#include "wx/wfstream.h"
#include "wx/mstream.h"
#include "wx/datstrm.h"

#include "imageutils/imageblock.h"

/*
 * Stores information about an image, in binary in-memory form
 */

wxImageBlock::wxImageBlock()
{
    Init();
}

wxImageBlock::wxImageBlock(const wxImageBlock& block):wxObject()
{
    Init();
    Copy(block);
}

wxImageBlock::~wxImageBlock()
{
    if (m_data)
    {
        delete[] m_data;
        m_data = NULL;
    }
}

void wxImageBlock::Init()
{
    m_data = NULL;
    m_dataSize = 0;
    m_imageType = -1;
}

void wxImageBlock::Clear()
{
    delete[] m_data;
    m_data = NULL;
    m_dataSize = 0;
    m_imageType = -1;
}


// Load the original image into a memory block.
// If the image is not a JPEG, we must convert it into a JPEG
// to conserve space.
// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
// load the image a 2nd time.

bool wxImageBlock::MakeImageBlock(const wxString& filename, int imageType, wxImage& image, bool convertToJPEG)
{
    m_imageType = imageType;

    wxString filenameToRead(filename);
    bool removeFile = false;

    if (imageType == -1)
        return false; // Could not determine image type

    if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
    {
        wxString tempFile;
        bool success = wxGetTempFileName(_("image"), tempFile) ;

        wxASSERT(success);

        wxUnusedVar(success);

        image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
        filenameToRead = tempFile;
        removeFile = true;

        m_imageType = wxBITMAP_TYPE_JPEG;
    }
    wxFile file;
    if (!file.Open(filenameToRead))
        return false;

    m_dataSize = (size_t) file.Length();
    file.Close();

    if (m_data)
        delete[] m_data;
    m_data = ReadBlock(filenameToRead, m_dataSize);

    if (removeFile)
        wxRemoveFile(filenameToRead);

    return (m_data != NULL);
}

// Make an image block from the wxImage in the given
// format.
bool wxImageBlock::MakeImageBlock(const wxString& filename, wxImage& image, int imageType, int quality)
{
    if (image.LoadFile(filename, imageType))
        return MakeImageBlock(image, imageType, quality);
    else
        return false;
}

// Make an image block from the wxImage in the given
// format.
bool wxImageBlock::MakeImageBlock(wxImage& image, int imageType, int quality)
{
    m_imageType = imageType;
    image.SetOption(wxT("quality"), quality);

    if (imageType == -1)
        return false; // Could not determine image type

    wxString tempFile;
    bool success = wxGetTempFileName(_("image"), tempFile) ;

    wxASSERT(success);
    wxUnusedVar(success);

    if (!image.SaveFile(tempFile, m_imageType))
    {
        if (wxFileExists(tempFile))
            wxRemoveFile(tempFile);
        return false;
    }

    wxFile file;
    if (!file.Open(tempFile))
        return false;

    m_dataSize = (size_t) file.Length();
    file.Close();

    if (m_data)
        delete[] m_data;
    m_data = ReadBlock(tempFile, m_dataSize);

    wxRemoveFile(tempFile);

    return (m_data != NULL);
}

// Make an image block from the wxImage in the given
// format.
bool wxImageBlock::MakeImageBlock(const wxImage& image, int imageType, int quality)
{
    wxImage im(image);
    return MakeImageBlock((wxImage&) im, imageType, quality);
}

// Write to a file
bool wxImageBlock::Write(const wxString& filename)
{
    return WriteBlock(filename, m_data, m_dataSize);
}

// Write to a stream
bool wxImageBlock::Write(wxOutputStream& stream)
{
    wxDataOutputStream dataStream(stream);
    return Write(stream, dataStream);
}

bool wxImageBlock::Write(wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    dataStream << m_imageType;
    dataStream << (wxUint32) m_dataSize;

    if (m_dataSize > 0)
        return WriteBlock(stream, m_data, m_dataSize);
    else
        return false;
}

// Read from a stream
bool wxImageBlock::Read(wxInputStream& stream)
{
    wxDataInputStream dataStream(stream);
    return Read(stream, dataStream);
}

bool wxImageBlock::Read(wxInputStream& stream, wxDataInputStream& dataStream)
{
    if (m_data)
        delete[] m_data;
    m_data = NULL;

    dataStream >> m_imageType;

    wxUint32 dataSize;
    dataStream >> dataSize;
    m_dataSize = dataSize;

    if (m_dataSize > 0)
        m_data = ReadBlock(stream, m_dataSize);
    return (m_data != NULL);
}

void wxImageBlock::Copy(const wxImageBlock& block)
{
    m_imageType = block.m_imageType;
    if (m_data)
    {
        delete[] m_data;
        m_data = NULL;
    }
    m_dataSize = block.m_dataSize;
    if (m_dataSize == 0)
        return;

    m_data = new unsigned char[m_dataSize];
    unsigned int i;
    for (i = 0; i < m_dataSize; i++)
        m_data[i] = block.m_data[i];
}

//// Operators
void wxImageBlock::operator=(const wxImageBlock& block)
{
    Copy(block);
}

// Load a wxImage from the block
bool wxImageBlock::Load(wxImage& image)
{
    if (!m_data)
        return false;

    // Read in the image.
#if 1
    wxMemoryInputStream mstream(m_data, m_dataSize);
    bool success = image.LoadFile(mstream, GetImageType());
#else
    wxString tempFile;
    bool success = wxGetTempFileName(_("image"), tempFile) ;
    wxASSERT(success);

    if (!WriteBlock(tempFile, m_data, m_dataSize))
    {
        return false;
    }
    success = image.LoadFile(tempFile, GetImageType());
    wxRemoveFile(tempFile);
#endif

    return success;
}

// Write data in hex to a stream
bool wxImageBlock::WriteHex(wxOutputStream& stream)
{
    wxString hex;
    int i;
    for (i = 0; i < (int) m_dataSize; i++)
    {
        hex = wxDecToHex(m_data[i]);
        wxCharBuffer buf = hex.ToAscii();

        stream.Write((const char*) buf, hex.Length());
    }

    return true;
}

// Read data in hex from a stream
bool wxImageBlock::ReadHex(wxInputStream& stream, int length, int imageType)
{
    int dataSize = length/2;

    if (m_data)
        delete[] m_data;

    wxString str(wxT("  "));
    m_data = new unsigned char[dataSize];
    int i;
    for (i = 0; i < dataSize; i ++)
    {
        str[0] = stream.GetC();
        str[1] = stream.GetC();

        m_data[i] = (unsigned char)wxHexToDec(str);
    }

    m_dataSize = dataSize;
    m_imageType = imageType;

    return true;
}


// Allocate and read from stream as a block of memory
unsigned char* wxImageBlock::ReadBlock(wxInputStream& stream, size_t size)
{
    unsigned char* block = new unsigned char[size];
    if (!block)
        return NULL;

    stream.Read(block, size);

    return block;
}

unsigned char* wxImageBlock::ReadBlock(const wxString& filename, size_t size)
{
    wxFileInputStream stream(filename);
    if (!stream.Ok())
        return NULL;

    return ReadBlock(stream, size);
}

// Write memory block to stream
bool wxImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
{
    stream.Write((void*) block, size);
    return stream.IsOk();

}

// Create stream, prompting for a retry, and if failing, returning false.
// If it returns false, there is no need for the app to delete the stream:
// it will have been deleted.
static bool wxImageBlockSafelyCreateFileOutputStream(wxFileOutputStream** stream, const wxString& filename, wxWindow* parent = NULL, const wxString& msg = wxEmptyString, const wxString& title = wxEmptyString)
{
    wxString msg1(msg);
    wxString title1(title);

    if (msg1.IsEmpty())
        msg1 = _("Sorry, could not write to %s.\nTry again?");
    if (title1.IsEmpty())
        title1 = _("File Saving Problem");

    if (!parent)
        parent = wxTheApp->GetTopWindow();

    while (true)
    {
        wxLogNull logNull;
        *stream = new wxFileOutputStream(filename);
        if ((*stream)->Ok())
            return true;

        delete (*stream);
        (*stream) = NULL;

        if (wxNO == wxMessageBox(wxString::Format(msg1, filename.c_str()), title1, wxYES_NO|wxICON_QUESTION, parent))
            return false;
    }

    return false;
}

// Write memory block to file
bool wxImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
{
    wxFileOutputStream* stream = NULL;
    
    if (!wxImageBlockSafelyCreateFileOutputStream(& stream, filename))
        return false;

    bool success = WriteBlock(* stream, block, size);
    delete stream;

    return success;
}

// Returns the image type, or -1, determined from the extension.
int wxImageBlock::DetermineImageType(const wxString& filename)
{
    wxString path, name, ext;

    wxSplitPath(filename, & path, & name, & ext);

    ext.MakeLower();
    if (ext == _T("jpg") || ext == _T("jpeg"))
        return wxBITMAP_TYPE_JPEG;
    else if (ext == _T("gif"))
        return wxBITMAP_TYPE_GIF;
    else if (ext == _T("bmp"))
        return wxBITMAP_TYPE_BMP;
    else if (ext == _T("png"))
        return wxBITMAP_TYPE_PNG;
    else if (ext == _T("xpm"))
        return wxBITMAP_TYPE_XPM;
    else if (ext == _T("pcx"))
        return wxBITMAP_TYPE_PCX;
    else if (ext == _T("tif") || ext == _T("tiff"))
        return wxBITMAP_TYPE_TIF;
#ifdef __WXMSW__
    else if (ext == wxT("ico"))
        return wxBITMAP_TYPE_ICO;
#endif
    else
        return -1;
}

// Return an extension for the given file type
wxString wxImageBlock::DetermineExtension(int imageType)
{
    wxString ext;
    switch (imageType)
    {
    case wxBITMAP_TYPE_JPEG:
        ext = wxT("jpg");
        break;
    case wxBITMAP_TYPE_PNG:
        ext = wxT("png");
        break;
    case wxBITMAP_TYPE_TIF:
        ext = wxT("tif");
        break;
    case wxBITMAP_TYPE_PCX:
        ext = wxT("pcx");
        break;
    case wxBITMAP_TYPE_ICO:
        ext = wxT("ico");
        break;
    case wxBITMAP_TYPE_XPM:
        ext = wxT("xpm");
        break;
    case wxBITMAP_TYPE_GIF:
        ext = wxT("gif");
        break;
    case wxBITMAP_TYPE_BMP:
        ext = wxT("bmp");
        break;
    }
    if (ext.IsEmpty())
        ext = wxT("png");
    return ext;
}


